#ifndef _EXPERIMENT_HPP_
#define _EXPERIMENT_HPP_
#include "definitions.h"
#include "regeneration_engine.hpp"
#include "random.h"
#include "time_providers.hpp"
#include "infinite_server.hpp"
#include "server.hpp"
#include <vector>
#include <iostream>
#include <iomanip>
using namespace std;

//net definitions
#define N_JOBS 5
#define CPU2DELAY 0.05f
#define CPU2DISK 0.1f
#define Z 10.0f //terms
#define S1 0.03f //cpu
#define S2 0.5f //disk

void experiment(interval_t &output, int seed, double error) {
  //create the uniform(0,1) random number generator
  rng_t rng_(seed);
  rng01_t rng(rng_, gm_uniform_double_01_t());

  //create time-providers
  vector<double> params;

  //terminals: Neg. Exp.
  params.push_back(1.0/Z);
  TP_NegExp tp0(params, rng, "term_negexp");
  params.clear();

  /*
  //CPU: Neg. Exp.
  params.push_back(1.0/S1);
  TP_NegExp tp1(params, rng, "cpu_negexp");
  */
  /*
  //CPU: Erlang
  params.push_back(3);
  params.push_back(1.0/S1);
  TP_Erlang tp1(params, rng, "cpu_erlang");
  */
  //CPU: Custom
  params.push_back(0);
  params.push_back(10e-03);
  params.push_back(0);
  params.push_back(20e-03);
  params.push_back(15e-03);
  params.push_back(3e-06);
  params.push_back(0);
  params.push_back(30e-03);
  TP_Custom01 tp1(params, rng, "cpu_strange");
  params.clear();

  //disk: Neg. Exp.
  params.push_back(1.0/S2);
  TP_NegExp tp2(params, rng, "disk_negexp");

  //create queues
  InfiniteServer *delay = new InfiniteServer(&tp0, rng, "delay");
  Server *cpu = new Server(&tp1, rng, "CPU");
  Server *disk = new Server(&tp2, rng, "I/O");

  //set topology
  delay->add_default_destination(cpu);
  cpu->add_destination(delay, CPU2DELAY);
  cpu->add_destination(disk, CPU2DISK);
  cpu->add_default_destination(cpu);
  disk->add_default_destination(cpu);

  //create jobs
  int n_jobs = N_JOBS;
  vector<Job> jobs(n_jobs);

  //set the sim. engine
  Engine engine;
  vector<Queue *> queues;
  queues.push_back(delay);
  queues.push_back(cpu);
  queues.push_back(disk);
  engine.add_queues(queues);

  //put all jobs into delay
  for(int i=0; i<n_jobs; ++i)
    delay->arrival(&jobs[i]);

#ifdef LOG
  //print initial state
  engine.print_state();
#endif

  //regenerative runs
  Regeneration_Engine re(engine);
  re.init(cpu, delay, n_jobs, MIN_REGC_LEN);
  re.run(delay, error, jobs);
  re.get_interval(&output);

#ifdef LOG
  //print ending state and stats
  engine.print_state();
  engine.stats(cerr);
#endif

  //clean-up
  for(unsigned int i=0; i<queues.size(); ++i)
    delete queues[i];
}
#endif
